package com.alibaba.citrus.ots.application.report.ability.facade.job;

import com.alibaba.citrus.ots.application.report.ability.repository.AllocateOrderDetailRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.BaseDataQueryRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.MonthSalesStockReportQueryRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.MonthSalesStockReportWriteRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.PlanContractQuantityRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.SalesOrderLineRepository;
import com.alibaba.citrus.ots.application.report.ability.repository.SalesPlanDetailRepository;
import com.alibaba.citrus.ots.application.report.ability.utils.DomUtil;
import com.alibaba.citrus.ots.application.report.ability.utils.PageRecordUtil;
import com.alibaba.citrus.ots.common.annotation.FacadeInvoker;
import com.alibaba.citrus.ots.common.exception.FacadeException;
import com.alibaba.citrus.ots.common.util.DateUtils;
import com.alibaba.citrus.ots.common.util.ThreadPageUtil;
import com.alibaba.citrus.ots.sales.plan.model.enums.ContractDataSourceEnum;
import com.alibaba.citrus.ots.sales.plan.model.enums.ContractStatDimensionEnum;
import com.alibaba.citrus.ots.sales.plan.model.enums.SalesPlanModelEnum;
import com.alibaba.citrus.ots.sales.plan.model.enums.SalesPlanStatusEnum;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.StringUtil;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.page.PageResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.middleware.cache.runtime.service.EpochCacheService;
import com.alibaba.lattice2.epoch.middleware.mq.runtime.EpochMessageService;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.nacos.common.utils.Objects;
import com.epoch.app.bcorder.model.dto.BaselineGetOrganizationById2Request;
import com.epoch.app.bcorder.model.dto.OrgMonthPlanQuantitySDO;
import com.epoch.app.bcorder.model.dto.OrganizationSDO;
import com.epoch.app.bcorder.model.dto.ScItemSDO;
import com.epoch.app.bcorder.model.dto.SearchCargoZoneListRequest;
import com.epoch.app.bcorder.model.dto.SearchCargoZoneListResponse;
import com.epoch.app.bcorder.platform_enhance.SaleOrderTag;
import com.epoch.app.bcorder.sales_plan.contract.model.Contract;
import com.epoch.app.bcorder.sales_plan.salesplan.model.SalesPlan;
import com.epoch.app.bcorder.service.BaseDataService;
import com.epoch.app.bcorder.service.UnifiedInventoryService;
import com.epoch.app.crb2btradecenter.domain.orderline.model.OrderLine;
import com.epoch.app.crplatformenhance.enums.OrganizationTypeEnum;
import com.epoch.app.crplatformenhance.model.dto.GetDownOrganizationListRequest;
import com.epoch.app.crplatformenhance.service.CrPlatformEnhanceService;
import com.epoch.app.crunifiedinventory.domain.allocateorder.model.AllocateOrder;
import com.epoch.app.crunifiedinventory.domain.allocateorderdetail.model.AllocateOrderDetail;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryForAnalysisRequest;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryTotalForAnalysisRequest;
import com.epoch.app.crunifiedinventory.model.dto.GetInventoryTotalForAnalysisResponse;
import com.epoch.app.crunifiedinventory.model.dto.InventoryForAnalysisResp;
import com.epoch.app.crunifiedinventory.model.dto.WmsOrder;
import com.epoch.app.crunifiedinventory.model.dto.WmsSubOrder;
import com.epoch.app.crunifiedinventory.service.CrUnifiedInventoryService;
import com.epoch.app.otsapplicationreportsdk.contants.OtsApplicationReportSdkConstants;
import com.epoch.app.otsapplicationreportsdk.model.dto.BatchQueryContractQuantityRequest;
import com.epoch.app.otsapplicationreportsdk.model.dto.CommPageQueryRequest;
import com.epoch.app.otsapplicationreportsdk.model.dto.MonthSalesStockReportSDO;
import com.epoch.app.otsapplicationreportsdk.model.dto.QueryMonthStockReportRequest;
import com.epoch.app.otsapplicationreportsdk.model.enums.DataMarkEnum;
import com.epoch.app.otsapplicationreportsdk.model.enums.InventoryTypeEnum;
import com.epoch.app.otsapplicationreportsdk.model.enums.ScItemGuaranteePeriodTypeEnum;
import com.epoch.app.otsapplicationreportsdk.monthsalesstockreportwrite.dto.GenerateMonthSalesStockReportJobRequest;
import com.epoch.app.otsapplicationreportsdk.monthsalesstockreportwrite.dto.GenerateReportMessageConsumerRequest;
import com.epoch.app.otsapplicationreportsdk.monthsalesstockreportwrite.dto.GenerateReportMessageSenderRequest;
import com.epoch.app.otsapplicationreportsdk.monthsalesstockreportwrite.service.MonthSalesStockReportWriteService;
import com.google.common.collect.Lists;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhoubc
 * @description: TODO
 * @date 2023/3/7 16:18
 */
@Component
public class MonthSalesStockReportSyncJob implements MonthSalesStockReportWriteService {

    Log log = Log.getLogger(MonthSalesStockReportSyncJob.class);

    @Resource
    BaseDataService baseDataService;
    @Resource
    BaseDataQueryRepository baseDataQueryRepository;
    @Resource
    CrPlatformEnhanceService crPlatformEnhanceService;
    @Resource
    UnifiedInventoryService unifiedInventoryService;
    @Resource
    PlanContractQuantityRepository contractQuantityRepository;
    @Resource
    SalesPlanDetailRepository salesPlanDetailRepository;
    @Resource
    AllocateOrderDetailRepository allocateOrderDetailRepository;
    @Resource
    SalesOrderLineRepository salesOrderLineRepository;
    @Resource
    CrUnifiedInventoryService crUnifiedInventoryService;
    @Resource
    MonthSalesStockReportWriteRepository monthSalesStockReportWriteRepository;
    @Resource
    MonthSalesStockReportQueryRepository monthSalesStockReportQueryRepository;
    @Resource
    EpochCacheService epochCacheService;
    @Resource
    EpochMessageService epochMessageService;

    @Resource(name="monthStockReportTaskExecutor")
    ThreadPoolTaskExecutor monthStockReportTaskExecutor;
    @Resource(name="salesPlanTaskExecutor")
    ThreadPoolTaskExecutor salesPlanTaskExecutor;
    @Resource(name="reportQueryDataExecutor")
    ThreadPoolTaskExecutor reportQueryDataExecutor;

    private final static int PageSize_1000 = 1000;
    private final static int PageSize_500 = 500;
    private final static int PageSize_300 = 300;
    public final static String RUNNING_TASK_REDIS_KEY = "RUNNING_STOCK_REPORT_TASK";
    private final String RUNNING_TASK_STATUS = "1";
    private final String COMPLTETE_TASK_STATUS = "2";
    private final String nykTjCompanyCode = "1900027440";
    private final String nykBjCompanyCode = "1900039345";
    private final String commerceCompanyCode = "1900021273";

    @Override
    @FacadeInvoker
    public Result<Void> generateReportMessageSender(GenerateReportMessageSenderRequest request) {
        if (StringUtils.isBlank(request.getStatisticalDate())) {
            throw new FacadeException("OTS-01-010-01-15-009");
        }
        if (StringUtils.isBlank(request.getOrgBusinessUnitId())) {
            throw new FacadeException("OTS-01-010-01-15-010");
        }
        if (request.getCheckJobs() != null && request.getCheckJobs()) {
            checkCanRunJob(request.getOrgBusinessUnitId());
        }
        String jsonRequest = JSONObject.toJSONString(request);
        epochMessageService.sendMessage("stock_report_message", "CR_STOCK_REPORT", "generateStockReportTask", jsonRequest);
        return Result.success(null);
    }

    @Override
    @FacadeInvoker
    public Result<Void> generateReportMessageConsumer(GenerateReportMessageConsumerRequest request) {
        GenerateMonthSalesStockReportJobRequest jobRequest = JSONObject.parseObject(request.getMessage(),GenerateMonthSalesStockReportJobRequest.class);
        generateMonthSalesStockReportJob(jobRequest);
        return Result.success(null);
    }

    @Override
    @FacadeInvoker
    public Result<Void> generateMonthSalesStockReportJob(GenerateMonthSalesStockReportJobRequest jobRequest) {
        StopWatch stopWatch = new StopWatch("generateMonthSalesStockReportJob");
        stopWatch.start("init(generateAllMonthSalesReportJobRequest)");
        JobInitParam jobInitParam = init(jobRequest);
        log.info("generateMonthSalesStockReportJob_statisticalDate_println={}", jobInitParam.getThisDay());
        stopWatch.stop();

        stopWatch.start("executeGenerateMonthSalesBasicReport");
        executeGenerateMonthSalesStockReport(jobInitParam);
        stopWatch.stop();

        log.info("generateMonthSalesStockReportJob_statisticalDate={}，consume={}，stopWatch={}", jobInitParam.getThisDay(), stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint());
        return Result.success(null);
    }

    private JobInitParam init(GenerateMonthSalesStockReportJobRequest jobRequest){
        //初始化日期参数
        Date todayDate = StringUtil.isNotEmpty(jobRequest.getStatisticalDate()) ? DateUtils.parse(jobRequest.getStatisticalDate()) : new Date();
        String thisDay = DateUtils.formatDate(todayDate, "yyyy-MM-dd");
        String thisMonth = DomUtil.getMonth(todayDate);
        String thisYear = DomUtil.getYear(todayDate);
        String thisMonthTimestamp = DomUtil.getMonthTimestamp(todayDate);
        String monthBeginTime = thisMonth+"-01 00:00:00";
        String todayEndTime = thisDay+" 23:59:59";

        //当天是否当月最终报表数据
        Integer isMonthDataMark = DomUtil.isLastDayOfMonth(todayDate)? DataMarkEnum.MONTH_FINAL_DATA.getCode():DataMarkEnum.MONTH_ACCUM_DATA.getCode();
        //是否同步库存数据
        Boolean isSyncInventory = thisDay.equals(DomUtil.getToday());

        String enterpriseGroupOrgCode = OtsApplicationReportSdkConstants.ENTERPRISE_GROUP_ORG_CODE()!=null?OtsApplicationReportSdkConstants.ENTERPRISE_GROUP_ORG_CODE():"6000";
        List<String> queryOrgCodes = Lists.newArrayList(nykTjCompanyCode,nykBjCompanyCode,commerceCompanyCode,enterpriseGroupOrgCode);
        Map<String, OrganizationSDO> queryOrgMap = baseDataQueryRepository.batchQueryOrganizationByOutCodes(queryOrgCodes);

        JobInitParam jobInitParam = new JobInitParam();
        jobInitParam.setOrgBusinessUnitId(jobRequest.getOrgBusinessUnitId());
        jobInitParam.setTodayDate(todayDate);
        jobInitParam.setThisDay(thisDay);
        jobInitParam.setThisMonth(thisMonth);
        jobInitParam.setThisYear(thisYear);
        jobInitParam.setThisMonthTimestamp(thisMonthTimestamp);
        jobInitParam.setMonth(String.valueOf(DomUtil.getMonthNum(todayDate)));
        jobInitParam.setMonthBeginTime(monthBeginTime);
        jobInitParam.setTodayEndTime(todayEndTime);
        jobInitParam.setIsMonthDataMark(isMonthDataMark);
        jobInitParam.setIsSyncInventory(isSyncInventory);
        jobInitParam.setEnterpriseGroupOrgId(queryOrgMap.get(enterpriseGroupOrgCode)!=null?queryOrgMap.get(enterpriseGroupOrgCode).getId():null);
        List<String> commerceNykOrgIds = queryOrgMap.entrySet().stream().filter(x->!enterpriseGroupOrgCode.equals(x.getValue().getCode())).map(x -> x.getValue().getId()).collect(Collectors.toList());
        jobInitParam.setCommerceNykOrgIds(commerceNykOrgIds);

        return jobInitParam;
    }

    private Boolean executeGenerateMonthSalesStockReport(JobInitParam jobInitParam){
        List<com.epoch.app.crplatformenhance.sdo.OrganizationSDO> executeCompanyOrgList = null;
        if(StringUtils.isBlank(jobInitParam.getOrgBusinessUnitId())){
            //查询常温事业部6000下所有分子公司组织
            GetDownOrganizationListRequest getDownOrganizationListRequest = new GetDownOrganizationListRequest();
            getDownOrganizationListRequest.setId(jobInitParam.getEnterpriseGroupOrgId());
            getDownOrganizationListRequest.setType(OrganizationTypeEnum.COMPANY.getCode());
            Result<List<com.epoch.app.crplatformenhance.sdo.OrganizationSDO>> mnCompanyOrgList = crPlatformEnhanceService.getDownOrganizationList(getDownOrganizationListRequest);
            executeCompanyOrgList = mnCompanyOrgList.getResult();
            if(CollectionUtils.isNotEmpty(jobInitParam.getCommerceNykOrgIds())){
                executeCompanyOrgList = Nullable.stream(executeCompanyOrgList).filter(x->!jobInitParam.getCommerceNykOrgIds().contains(x.getId())).collect(Collectors.toList());
            }
        }else{
            BaselineGetOrganizationById2Request request = new BaselineGetOrganizationById2Request();
            request.setId(jobInitParam.getOrgBusinessUnitId());
            Result<OrganizationSDO> orgResult = baseDataService.baselineGetOrganizationById2(request);
            if(orgResult != null && Objects.nonNull(orgResult.getResult())){
                com.epoch.app.crplatformenhance.sdo.OrganizationSDO org = new com.epoch.app.crplatformenhance.sdo.OrganizationSDO();
                BeanUtils.copyProperties(orgResult.getResult(),org);
                executeCompanyOrgList = Lists.newArrayList(org);
            }else{
                throw new FacadeException("OTS-01-010-01-15-005");
            }
        }

        List<Future<Map<String,Object>>> futureList = Lists.newArrayList();
        log.info("executeGenerateMonthSalesStockReport_orgs_size={}", executeCompanyOrgList.size());
        for(com.epoch.app.crplatformenhance.sdo.OrganizationSDO org : executeCompanyOrgList){
            String companyOrgId = org.getId();
            CompletableFuture<Map<String,Object>> future = CompletableFuture.supplyAsync(() -> {
                String taskTime = DateUtils.toDateStr(new Date(),DateUtils.DATETIME_FORMAT);
                updateRunningTaskStatus(jobInitParam.getThisDay(),companyOrgId,RUNNING_TASK_STATUS,taskTime,"");

                Map<String,Object> result = new HashMap<>();//TODO 简化为内部类
                result.put("orgId",companyOrgId);
                result.put("summaryAllocateSize",0);
                result.put("summaryPlanSize",0);
                result.put("summaryContractSize",0);
                result.put("salesDetailSize",0);
                result.put("inventoryDetailSize",0);
                result.put("outinDetailSize",0);
                result.put("mergeListSize",0);
                try {
                    //查询对应的销售组织
                    com.epoch.app.crplatformenhance.sdo.OrganizationSDO salesOrg = getSalesOrgByCompanyOrgId(companyOrgId);
                    if(salesOrg == null){
                        return result;
                    }
                    String salesOrgId = salesOrg.getId();

                    StopWatch sw = new StopWatch();
                    sw.start("getSalesOrderDetailList");
                    //取销售订单明细数据
                    List<OrderLine> salesDetailList = getSalesOrderDetailList(jobInitParam,salesOrgId);
                    result.put("salesDetailSize",salesDetailList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getSalesOrderDetailList_size={}", companyOrgId, salesDetailList.size());
                    sw.stop();
                    sw.start("getSummaryAllocateOrderList");
                    //取调拨订单汇总数据
                    List<AllocateOrderDetail> summaryAllocateList = getSummaryAllocateOrderList(jobInitParam,companyOrgId,salesOrgId);
                    result.put("summaryAllocateSize",summaryAllocateList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getSummaryAllocateOrderList_size={}", companyOrgId, summaryAllocateList.size());
                    sw.stop();
                    sw.start("getSummaryPlanScItemList");
                    //取计划回复汇总数据
                    List<OrgMonthPlanQuantitySDO> summaryPlanList = getSummaryPlanScItemList(jobInitParam,companyOrgId);
                    result.put("summaryPlanSize",summaryPlanList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getSummaryPlanScItemList_size={}", companyOrgId, summaryPlanList.size());
                    sw.stop();
                    sw.start("getSummaryCrmContractList");
                    //取CRM合同汇总数据
                    List<Contract> summaryContractList = getSummaryCrmContractList(jobInitParam,companyOrgId);
                    result.put("summaryContractSize",summaryContractList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getSummaryCrmContractList_size={}", companyOrgId, summaryContractList.size());

                    //无数据，不处理
                    if(CollectionUtils.isEmpty(summaryContractList) && CollectionUtils.isEmpty(summaryPlanList)
                            && CollectionUtils.isEmpty(summaryAllocateList) && CollectionUtils.isEmpty(salesDetailList)){
                        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_pull_data_all_empty", companyOrgId);
                        return result;
                    }
                    sw.stop();

                    sw.start("getOutinOrderDetailList");
                    //取调拨入库单数据
                    List<WmsSubOrder> outinDetailList = getOutinOrderDetailList(jobInitParam,companyOrgId,salesOrgId);
                    result.put("outinDetailSize",outinDetailList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getOutinOrderDetailList_size={}", companyOrgId, outinDetailList.size());
                    sw.stop();

                    sw.start("mergeList");
                    //合并
                    List<MonthSalesStockReportSDO> mergeList = buildMonthSalesStockReportList(jobInitParam,companyOrgId,
                            summaryContractList,summaryPlanList,summaryAllocateList,salesDetailList,outinDetailList);
                    result.put("mergeListSize",mergeList.size());
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_mergeList_size={}", companyOrgId, mergeList.size());
                    sw.stop();

                    sw.start("enichData");
                    //填充数据
                    enrichData(jobInitParam,companyOrgId,salesOrgId,mergeList,result);
                    sw.stop();

                    sw.start("writedata");
                    int reportTotal = getReportTotal(jobInitParam.getThisDay(),companyOrgId);
                    if (reportTotal == 0) {
                        monthSalesStockReportWriteRepository.batchCreate(mergeList);
                    } else {
                        Map<String, MonthSalesStockReportSDO> mergeMap = mergeList.stream().collect(Collectors.toMap(x -> fetchKey(x), Function.identity(), (v1, v2) -> v1));
                        List<MonthSalesStockReportSDO> batchDeleteList = new ArrayList<>();
                        compareOldReport(jobInitParam, companyOrgId, batchDeleteList, mergeMap, reportTotal);

                        //统计得到存在多余数据，删除
                        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_need_delete_list={}", companyOrgId, JSON.toJSONString(batchDeleteList));
                        if (CollectionUtils.isNotEmpty(batchDeleteList)) {
                            List<Long> ids = batchDeleteList.stream().map(MonthSalesStockReportSDO::getId).collect(Collectors.toList());
                            Integer count = monthSalesStockReportWriteRepository.batchDelete(ids);
                            log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_delete_request={},success_result={},statisticalDate={}", companyOrgId, JSON.toJSONString(ids), count, jobInitParam.getThisDay());
                        }

                        //批量创建新key报表数据
                        List<MonthSalesStockReportSDO> createList = mergeMap.values().stream().filter(x -> x.getHasProcess()==null || !x.getHasProcess()).collect(Collectors.toList());
                        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_need_batch_create_size={}", companyOrgId, createList.size());
                        if (CollectionUtils.isNotEmpty(createList)) {
                            monthSalesStockReportWriteRepository.batchCreate(createList);
                        }
                    }
                    sw.stop();
                    log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_finish_elapsed_time={},pullData={},stopWatch={}", companyOrgId, sw.getTotalTimeMillis(),JSON.toJSONString(result),sw.prettyPrint());
                } catch (Throwable e) {
                    log.error("salesStockSubtask"+companyOrgId+"=executeGenerateMonthSalesStockReport_future_occor_error ", e);
                } finally {
                    updateRunningTaskStatus(jobInitParam.getThisDay(),companyOrgId,COMPLTETE_TASK_STATUS,taskTime,DateUtils.toDateStr(new Date(),DateUtils.DATETIME_FORMAT));
                }
                return result;
            },monthStockReportTaskExecutor);
            futureList.add(future);
        }
        List<Map<String,Object>> todoList = new ArrayList<>();
        for (Future<Map<String,Object>> future : futureList) {
            try {
                Map result = future.get();
                todoList.add(result);
            } catch (Exception e) {
                log.error("executeGenerateMonthSalesStockReport_future_deal_get_occar_error ",e);
            }
        }

        int summaryAllocateSum = todoList.stream().mapToInt(x->(int)x.get("summaryAllocateSize")).sum();
        int summaryPlanSum = todoList.stream().mapToInt(x->(int)x.get("summaryPlanSize")).sum();
        int summaryContractSum = todoList.stream().mapToInt(x->(int)x.get("summaryContractSize")).sum();
        int salesDetailSum = todoList.stream().mapToInt(x->(int)x.get("salesDetailSize")).sum();
        int inventoryDetailSum = todoList.stream().mapToInt(x->(int)x.get("inventoryDetailSize")).sum();
        int outinDetailSum = todoList.stream().mapToInt(x->(int)x.get("outinDetailSize")).sum();
        int mergeListSum = todoList.stream().mapToInt(x->(int)x.get("mergeListSize")).sum();
        log.info("executeGenerateMonthSalesStockReport_finish_result={},statisticalDate={},summaryAllocateSum={},summaryPlanSum={},summaryContractSum={},salesDetailSum={},inventoryDetailSum={},outinDetailSum={},mergeListSum={}",
                JSONObject.toJSONString(todoList),jobInitParam.getThisDay(),summaryAllocateSum,summaryPlanSum,summaryContractSum,salesDetailSum,inventoryDetailSum,outinDetailSum,mergeListSum);
        return true;
    }

    private List<MonthSalesStockReportSDO> buildMonthSalesStockReportList(JobInitParam jobInitParam,String companyOrgId,
                                                                          List<Contract> summaryContractList,
                                                                          List<OrgMonthPlanQuantitySDO> summaryPlanList,
                                                                          List<AllocateOrderDetail> summaryAllocateList,
                                                                          List<OrderLine> salesDetailList,
                                                                          List<WmsSubOrder> outinDetailList){

        List<MonthSalesStockReportSDO> todoList = new ArrayList<>();
//        List<AllocateOrderDetail> hitAllocateList = new ArrayList<>();
//        List<OrderLine> hitSalesOrderList = new ArrayList<>();
//        List<Contract> hitContractList = new ArrayList<>();

        for (OrgMonthPlanQuantitySDO plan : summaryPlanList) {
            String planKey = plan.getScItemId();
            MonthSalesStockReportSDO sdo = initBasicReportSDO(jobInitParam,companyOrgId,planKey);
            enichPlanData(sdo,plan);

            //取调拨单汇总数据
            List<AllocateOrderDetail> hitAllocate = summaryAllocateList.stream()
                    .filter(x->planKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitAllocate)){
                //hitAllocateList.addAll(hitAllocate);
                summaryAllocateList.removeAll(hitAllocate);
                enrichAllocateData(sdo,hitAllocate.get(0));
            }else{
                initAllocateData(sdo);
            }
            //取销售单汇总数据
            List<OrderLine> hitSalesOrder = salesDetailList.stream()
                    .filter(x->planKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitSalesOrder)){
                //hitSalesOrderList.addAll(hitSalesOrder);
                salesDetailList.removeAll(hitSalesOrder);
                enichSalesOrderData(jobInitParam.getThisDay(),sdo,hitSalesOrder);
            }else{
                initSalesOrderData(sdo);
            }
            //取合同汇总数据
            List<Contract> hitContract = summaryContractList.stream()
                    .filter(x->planKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitContract)){
                //hitContractList.addAll(hitContract);
                summaryContractList.removeAll(hitContract);
                enichContractData(sdo,hitContract.get(0),jobInitParam.getMonth());
            }else{
                initContractData(sdo);
            }

            todoList.add(sdo);
        }

        //没有计划的调拨单处理
        List<AllocateOrderDetail> noHitAllocateList = summaryAllocateList;
        for (AllocateOrderDetail allocate : noHitAllocateList) {
            String allocateKey = allocate.getScItemId();
            MonthSalesStockReportSDO sdo = initBasicReportSDO(jobInitParam,companyOrgId,allocateKey);
            initPlanData(sdo);
            enrichAllocateData(sdo,allocate);

            //取销售单汇总数据
            List<OrderLine> hitSalesOrder = salesDetailList.stream()
                    .filter(x->allocateKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitSalesOrder)){
                //hitSalesOrderList.addAll(hitSalesOrder);
                salesDetailList.removeAll(hitSalesOrder);
                enichSalesOrderData(jobInitParam.getThisDay(),sdo,hitSalesOrder);
            }else{
                initSalesOrderData(sdo);
            }
            //取合同汇总数据
            List<Contract> hitContract = summaryContractList.stream()
                    .filter(x->allocateKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitContract)){
                //hitContractList.addAll(hitContract);
                summaryContractList.removeAll(hitContract);
                enichContractData(sdo,hitContract.get(0),jobInitParam.getMonth());
            }else{
                initContractData(sdo);
            }

            todoList.add(sdo);
        }

        //没有计划、调拨的销售单处理
        List<OrderLine> noHitSalesOrderList = salesDetailList;
        Map<String, List<OrderLine>> noHitSalesOrderMap = noHitSalesOrderList.stream().collect(Collectors.groupingBy(x->x.getScItemId()));
        for (Map.Entry<String,List<OrderLine>> m2 : noHitSalesOrderMap.entrySet()) {
            String allocateKey = m2.getKey();
            List<OrderLine> saleOrderLines = m2.getValue();
            MonthSalesStockReportSDO sdo = initBasicReportSDO(jobInitParam,companyOrgId,allocateKey);
            initPlanData(sdo);
            initAllocateData(sdo);
            enichSalesOrderData(jobInitParam.getThisDay(),sdo,saleOrderLines);

            //取合同汇总数据
            List<Contract> hitContract = summaryContractList.stream()
                    .filter(x->allocateKey.equals(x.getScItemId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(hitContract)){
                //hitContractList.addAll(hitContract);
                summaryContractList.removeAll(hitContract);
                enichContractData(sdo,hitContract.get(0),jobInitParam.getMonth());
            }else{
                initContractData(sdo);
            }

            todoList.add(sdo);
        }

        //没有计划、调拨、销售的合同处理
        List<Contract> noHitContractList = summaryContractList;
        for (Contract contract : noHitContractList) {
            String contractKey = contract.getScItemId();
            MonthSalesStockReportSDO sdo = initBasicReportSDO(jobInitParam,companyOrgId,contractKey);
            initPlanData(sdo);
            initAllocateData(sdo);
            initSalesOrderData(sdo);
            enichContractData(sdo,contract,jobInitParam.getMonth());

            todoList.add(sdo);
        }

        //补充出入库汇总数据
        Map<String, List<WmsSubOrder>> outinOrderMap = outinDetailList.stream().collect(Collectors.groupingBy(x->x.getGoodsId()));
        for (MonthSalesStockReportSDO sdo : todoList) {
            String scItemId = sdo.getScItemId();
            enrichOutinOrderData(sdo, outinOrderMap.get(scItemId));
        }

        return todoList;
    }

    private void enrichData(JobInitParam jobInitParam, String orgBusinessUnitId, String salesOrgId, List<MonthSalesStockReportSDO> todoList, Map<String,Object> result){
        Set<String> orgBusinessUnitIds = new HashSet<>();
        Set<String> scItemIds = new HashSet<>();

        todoList.forEach(x ->{
            if(StringUtil.isNotEmpty(x.getOrgBusinessUnitId())){
                orgBusinessUnitIds.add(x.getOrgBusinessUnitId());
            }
            if(StringUtil.isNotEmpty(x.getScItemId())){
                scItemIds.add(x.getScItemId());
            }
        });

        List<InventoryForAnalysisResp> inventoryList = getInventoryDetailList(jobInitParam,orgBusinessUnitId,salesOrgId,Lists.newArrayList(scItemIds));
        result.put("inventoryDetailSize",inventoryList.size());
        Map<String,List<InventoryForAnalysisResp>> inventoryMap = inventoryList.stream().collect(Collectors.groupingBy(x->x.getGoodsId()));

        Map<String, OrganizationSDO> businessUnitMap = baseDataQueryRepository.batchQueryOrganization(Lists.newArrayList(orgBusinessUnitIds));
        Map<String, ScItemSDO> scItemMap = baseDataQueryRepository.batchQueryScItemDetail(Lists.newArrayList(scItemIds));

        todoList.forEach(x ->{
            //补充库存数据
            enichInventoryData(x, inventoryMap.get(x.getScItemId()));

            OrganizationSDO orgBusinessUnit = businessUnitMap.get(x.getOrgBusinessUnitId());
            if(orgBusinessUnit != null){
                x.setOrgBusinessUnitName(orgBusinessUnit.getName());
            }

            try {
                ScItemSDO scItem = scItemMap.get(x.getScItemId());
                if(scItem != null){
                    x.setScItemCode(scItem.getOuterId());
                    x.setScItemName(scItem.getTitle());
                    x.setScItemRootCategoryId(scItem.getRootCategoryId());
                    x.setScItemRootCategoryName(scItem.getRootCategoryName());
                    if(scItem.getFeatures()!=null && scItem.getFeatures().get("guaranteePeriod")!=null){
                        String guaranteePeriod = (String) scItem.getFeatures().get("guaranteePeriod");
                        if(Integer.valueOf(guaranteePeriod)<=60){
                            x.setScItemGuaranteePeriodType(ScItemGuaranteePeriodTypeEnum.STORT_GUARANTEEPERIOD.getValue());
                        }else{
                            x.setScItemGuaranteePeriodType(ScItemGuaranteePeriodTypeEnum.LONG_GUARANTEEPERIOD.getValue());
                        }
                    }
                }

                if(x.getScItemGuaranteePeriodType() != null && CollectionUtils.isNotEmpty(x.getDoms())){
                    List<String> distinctAndSortList = x.getDoms().stream()
                            .map(dom->dom.substring(0,10)).distinct()
                            .sorted((s1,s2) -> {
                                if(DateUtils.parse(s1).before(DateUtils.parse(s2))){
                                    return -1;
                                }else{
                                    return 1;
                                }
                            }).collect(Collectors.toList());
                    if(x.getScItemGuaranteePeriodType().equals(ScItemGuaranteePeriodTypeEnum.LONG_GUARANTEEPERIOD.getValue())){
                        distinctAndSortList = distinctAndSortList.stream().map(dom->dom.substring(0,7)).distinct().collect(Collectors.toList());
                    }
                    distinctAndSortList = distinctAndSortList.size()>30?distinctAndSortList.subList(0,30):distinctAndSortList;
                    x.setInventoryProductionDate(JSONObject.toJSONString(distinctAndSortList));
                }
            } catch (Throwable e) {
                log.error("salesStockSubtask"+orgBusinessUnitId+"=executeGenerateMonthSalesStockReport_future_enich_item_occor_error ", e);
            }
        });
    }

    private void compareOldReport(JobInitParam jobInitParam,String bizUnitId,List<MonthSalesStockReportSDO> batchDeleteList,Map<String, MonthSalesStockReportSDO> mergeMap,int reportTotal){
        List<Future<Boolean>> futureList = Lists.newArrayList();
        int total = reportTotal;
        int totalPage = ThreadPageUtil.calculateTotalPage(total,PageSize_1000);
        for (int pageNo = 1; pageNo <= totalPage; pageNo++) {
            int start = (pageNo-1)*PageSize_1000;
            CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                QueryMonthStockReportRequest request = new QueryMonthStockReportRequest();
                request.setOrgBusinessUnitIds(Arrays.asList(bizUnitId));
                request.setStatisticalDate(jobInitParam.getThisDay());
                request.setStart(start);
                request.setLimit(PageSize_1000);
                PageResult<List<MonthSalesStockReportSDO>> respone = monthSalesStockReportQueryRepository.pageQueryList(request);
                if (respone==null || CollectionUtils.isEmpty(respone.getContent())){
                    return false;
                }
                respone.getContent().forEach(x->{
                    MonthSalesStockReportSDO mergeSDO = mergeMap.get(fetchKey(x));
                    if (mergeSDO == null) {
                        batchDeleteList.add(x);
                    } else {
                        if(!jobInitParam.getIsSyncInventory()){
                            mergeSDO.setInventoryQuantity(x.getInventoryQuantity());
                            mergeSDO.setInventoryProductionDate(x.getInventoryProductionDate());
                        }
                        if (!needUpdate(mergeSDO,x)) {
                            //log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_exist_no_update_existed={}", bizUnitId, JSON.toJSONString(x));
                        } else {
                            Integer count = monthSalesStockReportWriteRepository.update(mergeSDO);
                            log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_future_exist_need_update_sdo={},success_result={}", bizUnitId, JSON.toJSONString(mergeSDO), count);
                        }
                        mergeSDO.setHasProcess(true);//对应已修改数据库旧数据
                    }
                });
                return true;
            }, salesPlanTaskExecutor);
            futureList.add(future);
        }
        List<Boolean> result = new ArrayList<>();
        for (Future<Boolean> future : futureList){
            try {
                Boolean sub = future.get();
                result.add(sub);
            }catch (Exception e){
                log.error("salesStockSubtask"+bizUnitId+"=getSalesStockReportList_query_error_get_result_error ", e);
            }
        }
        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_compareOldReport_queryReport_future_result={},reportTotal={}", bizUnitId, JSONObject.toJSON(result), total);
    }

    //取报表总数
    private int getReportTotal(String statisticalDate, String bizUnitId){
        QueryMonthStockReportRequest queryReportRequest = new QueryMonthStockReportRequest();
        queryReportRequest.setOrgBusinessUnitIds(Arrays.asList(bizUnitId));
        queryReportRequest.setStatisticalDate(statisticalDate);
        queryReportRequest.setStart(0);
        queryReportRequest.setLimit(1);
        PageResult<List<MonthSalesStockReportSDO>> pageResult = monthSalesStockReportQueryRepository.pageQueryList(queryReportRequest);
        return pageResult.getTotal();
    }

    private MonthSalesStockReportSDO initBasicReportSDO(JobInitParam jobInitParam, String orgBusinessUnitId, String scItemId){
        MonthSalesStockReportSDO sdo = new MonthSalesStockReportSDO();
        sdo.setOrgBusinessUnitId(orgBusinessUnitId);
        sdo.setScItemId(scItemId);
        sdo.setStatisticalDate(jobInitParam.getThisDay());
        sdo.setDataMark(jobInitParam.isMonthDataMark);
        return sdo;
    }

    private void enichPlanData(MonthSalesStockReportSDO sdo,OrgMonthPlanQuantitySDO plan){
        Long monthPlanReplyQuantity = multiplyThousand(plan.getMonthFirstTaskCount()+plan.getMonthMiddleTaskCount()+plan.getMonthLastTaskCount());
        sdo.setMonthPlanReplyQuantity(monthPlanReplyQuantity);
    }

    private void initPlanData(MonthSalesStockReportSDO sdo){
        sdo.setMonthPlanReplyQuantity(0L);
    }

    private void enrichAllocateData(MonthSalesStockReportSDO sdo, AllocateOrderDetail order){
        Long orderQuantity = multiplyThousand(order.getQuantity());
        Long confirmQuantity = multiplyThousand(order.getConfirmQuantity());
        Long shipQuantity = multiplyThousand(order.getShipQuantity());
        Long receiveQuantity = multiplyThousand(order.getReceiveQuantity());
        sdo.setMonthAllocateOrderQuantity(orderQuantity);
        sdo.setMonthAllocateUnshippedQuantity(confirmQuantity-shipQuantity);
        sdo.setMonthAllocateUnconfirmedQuantity(orderQuantity-confirmQuantity);
        sdo.setMonthAllocateReceivingQuantity(receiveQuantity);
        sdo.setMonthAllocateOnWayQuantity(shipQuantity-receiveQuantity);
    }

    private void initAllocateData(MonthSalesStockReportSDO sdo){
        sdo.setMonthAllocateOrderQuantity(0L);
        sdo.setMonthAllocateUnshippedQuantity(0L);
        sdo.setMonthAllocateUnconfirmedQuantity(0L);
        sdo.setMonthAllocateReceivingQuantity(0L);
        sdo.setMonthAllocateOnWayQuantity(0L);
    }

    private void enichSalesOrderData(String statisticalDate,MonthSalesStockReportSDO sdo,List<OrderLine> orderLines){
        BigDecimal monthSalesOrderQuantity = new BigDecimal(0);
        BigDecimal daySalesOrderQuantity = new BigDecimal(0);
        for (OrderLine line : orderLines){
            //单位转换，发货数量
            BigDecimal quantity = unitConvertQuantity(line.getDeliveryAmount(),line.getUnitConvert());

            //标准退货订单
            if(SaleOrderTag.STD_RETURN_ORDER.getCode().equals(line.getOrderLineType())) {
                monthSalesOrderQuantity = monthSalesOrderQuantity.subtract(quantity);
            //标准订单，车销订单，现金订单
            }else{
                monthSalesOrderQuantity = monthSalesOrderQuantity.add(quantity);
            }

            //今日订单量
            if(DomUtil.equalsDate(line.getGmtCreate(),DateUtils.parse(statisticalDate))){
                if(SaleOrderTag.STD_RETURN_ORDER.getCode().equals(line.getOrderLineType())) {
                    daySalesOrderQuantity = daySalesOrderQuantity.subtract(quantity);
                }else{
                    daySalesOrderQuantity = daySalesOrderQuantity.add(quantity);
                }
            }
        }
        sdo.setMonthSalesOrderQuantity(multiplyThousand(monthSalesOrderQuantity).longValue());
        sdo.setDaySalesOrderQuantity(multiplyThousand(daySalesOrderQuantity).longValue());
    }

    private void initSalesOrderData(MonthSalesStockReportSDO sdo){
        sdo.setMonthSalesOrderQuantity(0L);
        sdo.setDaySalesOrderQuantity(0L);
    }

    private void enichContractData(MonthSalesStockReportSDO sdo,Contract contract,String month){
        sdo.setMonthContractQuantity(multiplyThousand(getContractQuantity(month,contract)));
    }

    private void initContractData(MonthSalesStockReportSDO sdo){
        sdo.setMonthContractQuantity(0L);
    }

    public Long getContractQuantity(String month, Contract contract){
        Integer quantity = 0;
        switch (month){
            case "1" :
                quantity = contract.getJanQuantity();
                break;
            case "2" :
                quantity = contract.getFebQuantity();
                break;
            case "3" :
                quantity = contract.getMarQuantity();
                break;
            case "4" :
                quantity = contract.getAprQuantity();
                break;
            case "5" :
                quantity = contract.getMayQuantity();
                break;
            case "6" :
                quantity = contract.getJuneQuantity();
                break;
            case "7" :
                quantity = contract.getJulyQuantity();
                break;
            case "8" :
                quantity = contract.getAugQuantity();
                break;
            case "9" :
                quantity = contract.getSeptQuantity();
                break;
            case "10" :
                quantity = contract.getOctQuantity();
                break;
            case "11" :
                quantity = contract.getNovQuantity();
                break;
            case "12" :
                quantity = contract.getDecQuantity();
                break;
        }
        if(quantity == null){
            quantity = 0;
        }
        return quantity.longValue();
    }

    private BigDecimal unitConvertQuantity(String quantity, String unitConvert){
        if(StringUtils.isBlank(quantity) ){
            return BigDecimal.ZERO;
        }
        if(!"1".equals(unitConvert)){
            BigDecimal jian = new BigDecimal(quantity).divide(new BigDecimal(unitConvert),3, RoundingMode.HALF_UP);
            return jian;
        }
        return new BigDecimal(quantity);
    }

    private void enichInventoryData(MonthSalesStockReportSDO sdo,List<InventoryForAnalysisResp> inventoryLines){
        if(CollectionUtils.isEmpty(inventoryLines)){
            sdo.setInventoryQuantity(0L);
            sdo.setDoms(new ArrayList<>());
            return;
        }
        Long inventoryQuantity = inventoryLines.stream().mapToLong(x->x.getTotalQuantity()).sum();
        sdo.setInventoryQuantity(inventoryQuantity);
        List<String> doms = inventoryLines.stream().filter(x->StringUtils.isNotBlank(x.getDom())).map(x->x.getDom()).collect(Collectors.toList());
        sdo.setDoms(doms);
    }

    private void enrichOutinOrderData(MonthSalesStockReportSDO sdo, List<WmsSubOrder> wmsSubOrders){
        if(CollectionUtils.isEmpty(wmsSubOrders)){
            return;
        }
        wmsSubOrders = wmsSubOrders.stream().sorted((s1,s2) -> new BigDecimal(s2.getPostingTime()).compareTo(new BigDecimal(s1.getPostingTime()))).collect(Collectors.toList());
        sdo.setRecentArrivedDate(wmsSubOrders.get(0).getPostingTime());
    }

    private Long multiplyThousand(Object quantity){
        if(quantity!=null){
            BigDecimal b = null;
            if(quantity instanceof Integer){
                b = new BigDecimal((Integer)quantity);
            }else if(quantity instanceof Long){
                b = new BigDecimal((Long)quantity);
            }else if(quantity instanceof String){
                b = new BigDecimal((String)quantity);
            }else if(quantity instanceof BigDecimal){
                b = (BigDecimal)quantity;
            }else{
                return 0L;
            }
            return b.multiply(DomUtil.ONE_THOUSAND_BIGDECIMAL).longValue();
        }
        return 0L;
    }

    private String fetchKey(MonthSalesStockReportSDO x){
        StringBuilder key = new StringBuilder();
        key.append(x.getOrgBusinessUnitId())
                .append("#")
                .append(x.getScItemId());
        return key.toString();
    }

    //判断是否需要更新
    private boolean needUpdate(MonthSalesStockReportSDO todo, MonthSalesStockReportSDO existed){
        String todoInventoryProductionDate = StringUtils.isBlank(todo.getInventoryProductionDate())?"":todo.getInventoryProductionDate();
        String existedInventoryProductionDate = StringUtils.isBlank(existed.getInventoryProductionDate())?"":existed.getInventoryProductionDate();
        String todoRecentArrivedDate = StringUtils.isBlank(todo.getRecentArrivedDate())?"":todo.getRecentArrivedDate();
        String existedRecentArrivedDate = StringUtils.isBlank(existed.getRecentArrivedDate())?"":existed.getRecentArrivedDate();

        return !todo.getMonthContractQuantity().equals(existed.getMonthContractQuantity()) || !todo.getMonthPlanReplyQuantity().equals(existed.getMonthPlanReplyQuantity())
                || !todo.getMonthAllocateOrderQuantity().equals(existed.getMonthAllocateOrderQuantity()) || !todo.getMonthAllocateUnconfirmedQuantity().equals(existed.getMonthAllocateUnconfirmedQuantity())
                || !todo.getMonthAllocateUnshippedQuantity().equals(existed.getMonthAllocateUnshippedQuantity())|| !todo.getMonthAllocateReceivingQuantity().equals(existed.getMonthAllocateReceivingQuantity())
                || !todo.getMonthAllocateOnWayQuantity().equals(existed.getMonthAllocateOnWayQuantity()) || !todo.getMonthSalesOrderQuantity().equals(existed.getMonthSalesOrderQuantity())
                || !todo.getDaySalesOrderQuantity().equals(existed.getDaySalesOrderQuantity()) || !todo.getInventoryQuantity().equals(existed.getInventoryQuantity())
                || !todoInventoryProductionDate.equals(existedInventoryProductionDate) || !todoRecentArrivedDate.equals(existedRecentArrivedDate);
    }

    //并行拉取分子公司x货品维度汇总CRM合同数据
    private List<Contract> getSummaryCrmContractList(JobInitParam jobInitParam, String orgBusinessUnitId){
        PageRecordUtil pageUtil = new PageRecordUtil();
        pageUtil.resetPageParam();
        pageUtil.setPageSize(PageSize_1000);
        BatchQueryContractQuantityRequest request = new BatchQueryContractQuantityRequest();
        request.setContractYear(jobInitParam.getThisYear());
        request.setType(ContractStatDimensionEnum.COMPANY_SC_ITEM.getCode());
        request.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId));
        request.setDataSource(ContractDataSourceEnum.CRM.getCode());
        request.setLimit(pageUtil.getPageSize());
        List<Contract> list = new ArrayList<>();
        for (int i = 1; i <= pageUtil.getPage(); i++) {
            request.setStart(pageUtil.getStart());
            Result<List<Contract>> planItemResult = contractQuantityRepository.statisticalContractGroupByCompanyScItem(request);
            if(planItemResult != null && CollectionUtils.isNotEmpty(planItemResult.getResult())){
                list.addAll(planItemResult.getResult());
            }
            if(i == 1){
                pageUtil.setTotalPages(planItemResult!=null?planItemResult.getTotal():0);
            }
            pageUtil.turnPage();
        }
        return list;
    }

    //拉取分子公司x货品维度月度计划汇总CRM回复量数据
    private List<OrgMonthPlanQuantitySDO> getSummaryPlanScItemList(JobInitParam jobInitParam, String orgBusinessUnitId){
        String planMonth = jobInitParam.getThisMonthTimestamp();
        //分子公司货品CRM回复量 状态 >= 1
        List<OrgMonthPlanQuantitySDO> planScItemList = getPlanScItemList(planMonth,orgBusinessUnitId);
        if(CollectionUtils.isEmpty(planScItemList)){
            return Lists.newArrayList();
        }

        //分子公司计划调整单
        List<SalesPlan> adjustPlanList = getAdjustPlanList(planMonth,orgBusinessUnitId);
        //计划不是发布状态且没有对应的调整主单,说明计划没有发布过，return    --先判断计划有没有发布过，
        if(CollectionUtils.isEmpty(adjustPlanList)
                && !SalesPlanStatusEnum.PUBLISHED.getCode().equals(planScItemList.get(0).getStatus())){
            return Lists.newArrayList();
        }

        //分子公司货品月度累计调整量 状态 != -1
        List<OrgMonthPlanQuantitySDO> planItemGetAdjustList = getPlanScItemListGetAdjust(planMonth,orgBusinessUnitId);
        //TODO  多余一步
        Map<String,List<OrgMonthPlanQuantitySDO>> adjustQuantityMap = Nullable.stream(planItemGetAdjustList).collect(Collectors.groupingBy(OrgMonthPlanQuantitySDO::getOrganizationId));

        //循环计划货品数据,(1计划已发布，2计划非发布且没有发布的调整回复，3计划发布且有发布的调整回复)
        //多次调整回复，--3计划发布且有发布的调整回复
        //一次调整回复，--3计划发布且有发布的调整回复
        //没有调整回复，--1计划已发布，2计划非发布且没有发布的调整回复
        planScItemList.forEach(x ->{
            List<OrgMonthPlanQuantitySDO> companyAdjustScItemList = adjustQuantityMap.get(x.getOrganizationId());
            List<OrgMonthPlanQuantitySDO> adjustScItemList = Nullable.stream(companyAdjustScItemList).filter(j -> j.getScItemId().equals(x.getScItemId())).collect(Collectors.toList());

            //多次调整
            if(adjustScItemList.size()>1){
                adjustScItemList = adjustScItemList.stream().sorted((s1,s2) -> new BigDecimal(DateUtils.dateToStamp(s2.getGmtCreate())).compareTo(new BigDecimal(DateUtils.dateToStamp(s1.getGmtCreate())))).collect(Collectors.toList());
                OrgMonthPlanQuantitySDO recentAdjust = adjustScItemList.get(0);
                OrgMonthPlanQuantitySDO beforeAdjust = adjustScItemList.get(1);
                List<SalesPlan> filterAdjustPlan = adjustPlanList.stream().filter(plan->plan.getId().toString().equals(recentAdjust.getAdjustId())).collect(Collectors.toList());
                //最后次调整已发布
                if(CollectionUtils.isNotEmpty(filterAdjustPlan) && SalesPlanStatusEnum.PUBLISHED.getCode().equals(filterAdjustPlan.get(0).getStatus())){
                    x.setMonthFirstTaskCount(recentAdjust.getMonthFirstTaskCount()+recentAdjust.getMonthFirstAdjustReturnCount());
                    x.setMonthMiddleTaskCount(recentAdjust.getMonthMiddleTaskCount()+recentAdjust.getMonthMiddleAdjustReturnCount());
                    x.setMonthLastTaskCount(recentAdjust.getMonthLastTaskCount()+recentAdjust.getMonthLastAdjustReturnCount());
                }else{
                    x.setMonthFirstTaskCount(beforeAdjust.getMonthFirstTaskCount()+beforeAdjust.getMonthFirstAdjustReturnCount());
                    x.setMonthMiddleTaskCount(beforeAdjust.getMonthMiddleTaskCount()+beforeAdjust.getMonthMiddleAdjustReturnCount());
                    x.setMonthLastTaskCount(beforeAdjust.getMonthLastTaskCount()+beforeAdjust.getMonthLastAdjustReturnCount());
                }
            //一次调整
            }else if(adjustScItemList.size()==1){
                OrgMonthPlanQuantitySDO adjust = adjustScItemList.get(0);
                List<SalesPlan> filterAdjustPlan = adjustPlanList.stream().filter(plan->plan.getId().toString().equals(adjust.getAdjustId())).collect(Collectors.toList());
                //调整已发布
                if(CollectionUtils.isNotEmpty(filterAdjustPlan) && SalesPlanStatusEnum.PUBLISHED.getCode().equals(filterAdjustPlan.get(0).getStatus())){
                    x.setMonthFirstTaskCount(adjust.getMonthFirstTaskCount()+adjust.getMonthFirstAdjustReturnCount());
                    x.setMonthMiddleTaskCount(adjust.getMonthMiddleTaskCount()+adjust.getMonthMiddleAdjustReturnCount());
                    x.setMonthLastTaskCount(adjust.getMonthLastTaskCount()+adjust.getMonthLastAdjustReturnCount());
                }else{
                    x.setMonthFirstTaskCount(x.getMonthFirstReturnCount());
                    x.setMonthMiddleTaskCount(x.getMonthMiddleReturnCount());
                    x.setMonthLastTaskCount(x.getMonthLastReturnCount());
                }
            }else{
                x.setMonthFirstTaskCount(x.getMonthFirstReturnCount());
                x.setMonthMiddleTaskCount(x.getMonthMiddleReturnCount());
                x.setMonthLastTaskCount(x.getMonthLastReturnCount());
            }
        });
        return planScItemList;
    }

    //拉取分子公司x货品维度月度计划CRM回复量数据
    private List<OrgMonthPlanQuantitySDO> getPlanScItemList(String planMonth, String orgBusinessUnitId){
        PageRecordUtil pageUtil = new PageRecordUtil();
        pageUtil.resetPageParam();
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId));
        request.setPlanMonth(planMonth);
        request.setLimit(pageUtil.getPageSize());
        List<OrgMonthPlanQuantitySDO> list = new ArrayList<>();
        for (int i = 1; i <= pageUtil.getPage(); i++) {
            request.setStart(pageUtil.getStart());
            PageResult<List<OrgMonthPlanQuantitySDO>> planItemResult = salesPlanDetailRepository.pageStatisticalReturnCountByOrganiztion(request);
            if(i == 1){
                pageUtil.setTotalPages(planItemResult.getTotal());
            }
            list.addAll(planItemResult.getContent());
            pageUtil.turnPage();
        }
        return list;
    }

    //拉取分子公司x货品维度月度计划CRM调整量
    private List<OrgMonthPlanQuantitySDO> getPlanScItemListGetAdjust(String planMonth, String orgBusinessUnitId){
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId));
        request.setPlanMonth(planMonth);
        List<OrgMonthPlanQuantitySDO> list = salesPlanDetailRepository.queryAdjustCountByOrganiztion(request);
        return list;
    }

    //获取分子公司月度调整计划
    private List<SalesPlan> getAdjustPlanList(String planMonth, String orgBusinessUnitId){
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setOrgBusinessUnitCode(orgBusinessUnitId);
        request.setPlanMonth(planMonth);
        request.setModel(SalesPlanModelEnum.ADJUST_MODEL.getModel());
        request.setStart(0);
        request.setLimit(PageSize_500);
        List<SalesPlan> list = salesPlanDetailRepository.getAdjustPlanList(request);
        return list;
    }

    //拉取分子公司x货品维度汇总调拨订单数据
    private List<AllocateOrderDetail> getSummaryAllocateOrderList(JobInitParam jobInitParam, String orgBusinessUnitId, String salesOrgId){
        PageRecordUtil pageUtil = new PageRecordUtil();
        pageUtil.resetPageParam();
        pageUtil.setPageSize(PageSize_1000);
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId,salesOrgId));
        request.setBeginDate(jobInitParam.getMonthBeginTime());
        request.setEndDate(jobInitParam.getTodayEndTime());
        request.setLimit(pageUtil.getPageSize());
        List<AllocateOrderDetail> list = new ArrayList<>();
        for (int i = 1; i <= pageUtil.getPage(); i++) {
            request.setStart(pageUtil.getStart());
            Result<List<AllocateOrderDetail>> result = allocateOrderDetailRepository.statisticalAllocateOrderGroupByOrgScItem(request);
            if(result != null && CollectionUtils.isNotEmpty(result.getResult())){
                list.addAll(result.getResult());
            }
            if(i == 1){
                pageUtil.setTotalPages(result != null?result.getTotal():0);
            }
            pageUtil.turnPage();
        }
        return list;
    }

    //拉取分子公司x货品维度销售订单明细数据
    private List<OrderLine> getSalesOrderDetailList(JobInitParam jobInitParam, String orgSalesOrganizationId){
        List<Future<List<OrderLine>>> futureList = Lists.newArrayList();
        int total = getSalesOrderListTotal(jobInitParam, orgSalesOrganizationId);
        int totalPage = ThreadPageUtil.calculateTotalPage(total,PageSize_1000);
        for (int pageNo = 1; pageNo <= totalPage; pageNo++) {
            int start = (pageNo-1)*PageSize_1000;
            CompletableFuture<List<OrderLine>> future = CompletableFuture.supplyAsync(() -> {
                CommPageQueryRequest queryRequest = new CommPageQueryRequest();
                queryRequest.setOrganizationIds(Lists.newArrayList(orgSalesOrganizationId));
                queryRequest.setBeginDate(jobInitParam.getMonthBeginTime());
                queryRequest.setEndDate(jobInitParam.getTodayEndTime());
                queryRequest.setStart(start);
                queryRequest.setLimit(PageSize_1000);
                Result<List<OrderLine>> result = salesOrderLineRepository.queryQrderLineByCompleted(queryRequest);
                List<OrderLine> list = new ArrayList<>();
                if (result!=null && CollectionUtils.isNotEmpty(result.getResult())){
                    list = result.getResult();
                }
                return list;
            }, reportQueryDataExecutor);
            futureList.add(future);
        }
        List<OrderLine> result = new ArrayList<>();
        for (Future< List<OrderLine>> future : futureList){
            try {
                List<OrderLine> subList = future.get();
                result.addAll(subList);
            } catch (Exception e) {
                log.error("salesStock_getSalesOrderDetailList_query_error_get_result_error ", e);
            }
        }
        return result;
    }

    //拉取分子公司下的调拨单id查询入库单明细
    private List<WmsSubOrder> getOutinOrderDetailList(JobInitParam jobInitParam, String orgBusinessUnitId, String salesOrgId){
        List<Future<List<WmsOrder>>> futureList = Lists.newArrayList();
        int total = queryAllocateOrderIdsByNotReturnTotal(jobInitParam,orgBusinessUnitId,salesOrgId);
        int totalPage = ThreadPageUtil.calculateTotalPage(total,PageSize_300);
        for (int pageNo = 1; pageNo <= totalPage; pageNo++) {
            int start = (pageNo-1)*PageSize_300;
            CompletableFuture<List<WmsOrder>> future = CompletableFuture.supplyAsync(() -> {
                CommPageQueryRequest queryRequest = new CommPageQueryRequest();
                queryRequest.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId,salesOrgId));
                queryRequest.setBeginDate(jobInitParam.getMonthBeginTime());
                queryRequest.setEndDate(jobInitParam.getTodayEndTime());
                queryRequest.setStart(start);
                queryRequest.setLimit(PageSize_300);
                Result<List<AllocateOrder>> queryAllocateResult = allocateOrderDetailRepository.queryAllocateOrderIdsByNotReturn(queryRequest);
                List<String> outAllocateOrderIds = new ArrayList<>();
                if (queryAllocateResult!=null && CollectionUtils.isNotEmpty(queryAllocateResult.getResult())){
                    outAllocateOrderIds = queryAllocateResult.getResult().stream().map(x->{
                        if(StringUtils.isNotBlank(x.getNykId())){
                            return x.getNykId();
                        }else{
                            return x.getOutAllocateOrderId();
                        }
                    }).collect(Collectors.toList());
                }

                List<WmsOrder> wmsOrderList = getOutinOrdersByOutAllocateOrderIds(outAllocateOrderIds);
                log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getOutinOrderDetailList_future,outAllocateOrderIds={},wmsOrderList={}", orgBusinessUnitId,JSONObject.toJSONString(outAllocateOrderIds),JSONObject.toJSONString(wmsOrderList));
                return wmsOrderList;
            }, reportQueryDataExecutor);
            futureList.add(future);
        }
        List<WmsOrder> wmsOrderList = new ArrayList<>();
        for (Future<List<WmsOrder>> future : futureList){
            try {
                List<WmsOrder> subList = future.get();
                wmsOrderList.addAll(subList);
            }catch (Throwable e){
                log.error("salesStockSubtask"+orgBusinessUnitId+"=salesStock_getOutinOrderDetailList_query_error_get_result_error ", e);
            }
        }

        List<WmsSubOrder> result = new ArrayList<>();
        for (WmsOrder wmsOrder : wmsOrderList) {
            if(StringUtils.isBlank(wmsOrder.getPostingTime())||!StringUtils.isNumeric(wmsOrder.getPostingTime())){
                continue;
            }
            List<WmsSubOrder> wmsSubOrders = Nullable.stream(wmsOrder.getGoods()).map(x->{
                x.setPostingTime(wmsOrder.getPostingTime());
                return x;
            }).collect(Collectors.toList());
            result.addAll(wmsSubOrders);
        }
        return result;
    }

    private List<WmsOrder> getOutinOrdersByOutAllocateOrderIds(List<String> outAllocateOrderIds){
        List<WmsOrder> list = new ArrayList<>();
        if(CollectionUtils.isEmpty(outAllocateOrderIds)){
            return list;
        }
        PageRecordUtil pageUtil = new PageRecordUtil();
        pageUtil.resetPageParam();
        pageUtil.setPageSize(PageSize_1000);
        for (int i = 1; i <= pageUtil.getPage(); i++) {
            int start = pageUtil.getStart();
            Result<List<WmsOrder>> result = allocateOrderDetailRepository.queryOutinOrdersByOutAllocateOrderIds(outAllocateOrderIds, start, PageSize_1000);
            if(result != null && CollectionUtils.isNotEmpty(result.getResult())){
                list.addAll(result.getResult());
            }
            if(i == 1){
                pageUtil.setTotalPages(result != null?result.getTotal():0);
            }
            pageUtil.turnPage();
        }
        return list;
    }

    //拉取分子公司下的库存明细数据
    private List<InventoryForAnalysisResp> getInventoryDetailList(JobInitParam jobInitParam,String orgBusinessUnitId,String salesOrgId,List<String> scItemIds){
        List<InventoryForAnalysisResp> inventoryList = new ArrayList<>();
        if(!jobInitParam.getIsSyncInventory()){
            return inventoryList;
        }
        SearchCargoZoneListRequest searchCargoZoneListRequest = new SearchCargoZoneListRequest();
        searchCargoZoneListRequest.setSaleOrgId(salesOrgId);
        searchCargoZoneListRequest.setModel(3);
        searchCargoZoneListRequest.setTypeList(Lists.newArrayList("3","4"));
        searchCargoZoneListRequest.setStart(0);
        searchCargoZoneListRequest.setLimit(PageSize_500);
        SearchCargoZoneListResponse response = unifiedInventoryService.searchCargoZoneList(searchCargoZoneListRequest);
        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getInventorySearchCargoZoneList_result={}", orgBusinessUnitId,JSONObject.toJSONString(response));

        if(Objects.nonNull(response) && CollectionUtils.isNotEmpty(response.getResult())){
            List<String> warehouseAreaIds = response.getResult().stream().map(x->x.getId()).collect(Collectors.toList());

            List<Future<List<InventoryForAnalysisResp>>> futureList = Lists.newArrayList();
            int totalPage = ThreadPageUtil.calculateTotalPage(scItemIds.size(),PageSize_500);
            for (int pageNo = 1; pageNo <= totalPage; pageNo++) {
                int start = (pageNo -1) * PageSize_500;
                int end = pageNo == totalPage ? scItemIds.size(): start + PageSize_500;
                List<String> queryScItemIds = scItemIds.subList(start, end);

                int totalInventory = queryInventoryTotal(warehouseAreaIds,queryScItemIds);
                int totalInventoryPage = ThreadPageUtil.calculateTotalPage(totalInventory,PageSize_500);
                for (int inventoryPageNo = 1; inventoryPageNo <= totalInventoryPage; inventoryPageNo++) {
                    int finalPageNo = inventoryPageNo;
                    CompletableFuture<List<InventoryForAnalysisResp>> future = CompletableFuture.supplyAsync(() -> {
                        GetInventoryForAnalysisRequest request = new GetInventoryForAnalysisRequest();
                        request.setType(InventoryTypeEnum.WARE_HOUSE.getType());
                        request.setWarehouseAreaIdList(warehouseAreaIds);
                        request.setGoodsIds(queryScItemIds);
                        request.setSize(PageSize_500);
                        request.setPage(finalPageNo);
                        Result<List<InventoryForAnalysisResp>> resp = crUnifiedInventoryService.getInventoryForAnalysis(request);
                        List<InventoryForAnalysisResp> result = new ArrayList<>();
                        if (resp != null && CollectionUtils.isNotEmpty(resp.getResult())) {
                            result = resp.getResult().stream().filter(x->x.getTotalQuantity()>0).collect(Collectors.toList());
                        }
                        return result;
                    }, reportQueryDataExecutor);
                    futureList.add(future);
                }
            }
            for (Future<List<InventoryForAnalysisResp>> future : futureList){
                try {
                    List<InventoryForAnalysisResp> subList = future.get();
                    inventoryList.addAll(subList);
                }catch (Exception e){
                    log.error("salesStockSubtask{}=salesStock_getInventoryDetailList_query_error_get_result_error ", e);
                }
            }
        }
        log.info("salesStockSubtask{}=executeGenerateMonthSalesStockReport_getInventoryDetailList_size={}", orgBusinessUnitId,inventoryList.size());
        return inventoryList;
    }

    //获取分子公司x货品维度销售订单明细总数
    private int getSalesOrderListTotal(JobInitParam jobInitParam, String orgSalesOrganizationId){
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setStart(0);
        request.setLimit(1);
        request.setOrganizationIds(Lists.newArrayList(orgSalesOrganizationId));
        request.setBeginDate(jobInitParam.getMonthBeginTime());
        request.setEndDate(jobInitParam.getTodayEndTime());
        Result<List<OrderLine>> totalResult = salesOrderLineRepository.queryQrderLineByCompleted(request);
        return totalResult!=null?totalResult.getTotal():0;
    }

    //获取查询非退单的调拨单id总数
    private int queryAllocateOrderIdsByNotReturnTotal(JobInitParam jobInitParam, String orgBusinessUnitId, String salesOrgId){
        CommPageQueryRequest request = new CommPageQueryRequest();
        request.setStart(0);
        request.setLimit(1);
        request.setOrganizationIds(Lists.newArrayList(orgBusinessUnitId,salesOrgId));
        request.setBeginDate(jobInitParam.getMonthBeginTime());
        request.setEndDate(jobInitParam.getTodayEndTime());
        Result<List<AllocateOrder>> totalResult = allocateOrderDetailRepository.queryAllocateOrderIdsByNotReturn(request);
        return totalResult!=null?totalResult.getTotal():0;
    }

    //获取批次库存明细总数
    private int queryInventoryTotal(List<String> warehouseAreaIds, List<String> goodsIds){
        GetInventoryTotalForAnalysisRequest totalRequest = new GetInventoryTotalForAnalysisRequest();
        totalRequest.setType(InventoryTypeEnum.WARE_HOUSE.getType());
        totalRequest.setWarehouseAreaIdList(warehouseAreaIds);
        totalRequest.setGoodsIds(goodsIds);
        GetInventoryTotalForAnalysisResponse totalResult = crUnifiedInventoryService.getInventoryTotalForAnalysis(totalRequest);
        return totalResult!=null?totalResult.getResult():0;
    }

    private com.epoch.app.crplatformenhance.sdo.OrganizationSDO getSalesOrgByCompanyOrgId(String companyOrgId){
        GetDownOrganizationListRequest getDownOrganizationListRequest = new GetDownOrganizationListRequest();
        getDownOrganizationListRequest.setId(companyOrgId);
        getDownOrganizationListRequest.setType(OrganizationTypeEnum.SALES_ORGANIZATION.getCode());
        Result<List<com.epoch.app.crplatformenhance.sdo.OrganizationSDO>> salesOrgs = crPlatformEnhanceService.getDownOrganizationList(getDownOrganizationListRequest);
        List<com.epoch.app.crplatformenhance.sdo.OrganizationSDO> filterSalesOrgs = Nullable.stream(salesOrgs.getResult()).filter(x->x.getName().contains("销售组织")).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(filterSalesOrgs)){
            return filterSalesOrgs.get(0);
        }
        return null;
    }

    private boolean checkCanRunJob(String orgBusinessUnitId){
        List<String> orgCompanyTasks = getRunningTaskAllCompany();
        List<String> runningtasks = orgCompanyTasks.stream().filter(x->{
            String[] runningData = x.split("#");
            if("1".equals(runningData[1])){
                return true;
            }else{
                return false;
            }
        }).collect(Collectors.toList());
        if(runningtasks.size()>OtsApplicationReportSdkConstants.STOCK_REPORT_TASK_THREAD()){
            throw new FacadeException("OTS-01-010-01-15-006");
        }
        String thisCompanyTask = getRunningTaskByOrgId(orgCompanyTasks,orgBusinessUnitId);
        if(StringUtils.isNotBlank(thisCompanyTask)){
            String[] runningData = thisCompanyTask.split("#");//orgBusinessUnitId#status(1执行中,2完成)#taskTime(yyyy-MM-dd HH:mm:ss)#completeTime(yyyy-MM-dd HH:mm:ss)
            String status = runningData[1];
            if("1".equals(status)){
                throw new FacadeException("OTS-01-010-01-15-007");
            }
            Date startTime = DateUtils.string2Date(runningData[2],DateUtils.DATETIME_FORMAT);
            Date endTime = new Date();
            long betweenSeconds = endTime.getTime() - startTime.getTime();
            if(betweenSeconds < 30*60*1000){
                throw new FacadeException("OTS-01-010-01-15-008");
            }
        }
        return true;
    }
    private List<String> getRunningTaskAllCompany(){
        String json = epochCacheService.get(OtsApplicationReportSdkConstants.REDIS_CACHE_CODE(),RUNNING_TASK_REDIS_KEY);
        List<String> runningOrgCompany = new ArrayList<>();
        if(StringUtils.isNotBlank(json)){
            runningOrgCompany = JSONObject.parseObject(json, List.class);
        }
        return runningOrgCompany;
    }

    private String getRunningTaskByOrgId(List<String> runningOrgCompany,String orgBusinessUnitId){
        List<String> filterList = runningOrgCompany.stream().filter(x->{
            String[] runningData = x.split("#");//orgBusinessUnitId#status(1执行中,2完成)#taskTime(yyyy-MM-dd HH:mm:ss)#completeTime(yyyy-MM-dd HH:mm:ss)
            if(orgBusinessUnitId.equals(runningData[0])){
                return true;
            }else{
                return false;
            }
        }).collect(Collectors.toList());
        String result = null;
        if(CollectionUtils.isNotEmpty(filterList)){
            result = filterList.get(0);
        }
        return result;
    }

    private void updateRunningTaskStatus(String statisticalDate, String orgBusinessUnitId, String status, String taskTime, String completeTime){
        if(!DomUtil.getToday().equals(statisticalDate)){
            return;
        }
        List<String> runningOrgCompany = getRunningTaskAllCompany();
        String str = getRunningTaskByOrgId(runningOrgCompany,orgBusinessUnitId);
        if(StringUtils.isNotBlank(str)){
            String[] runningData = str.split("#");
            String oldCompleteTime = runningData.length>3?runningData[3]:"";
            if(StringUtils.isBlank(completeTime)){
                completeTime = oldCompleteTime;
            }

            runningOrgCompany.remove(str);
        }
        //orgBusinessUnitId#status(1执行中,2完成)#taskTime(yyyy-MM-dd HH:mm:ss)#completeTime(yyyy-MM-dd HH:mm:ss)
        runningOrgCompany.add(orgBusinessUnitId+"#"+status+"#"+taskTime+"#"+completeTime);
        epochCacheService.put(OtsApplicationReportSdkConstants.REDIS_CACHE_CODE(),RUNNING_TASK_REDIS_KEY,JSON.toJSONString(runningOrgCompany),30L, TimeUnit.DAYS);
    }

    @Data
    private class JobInitParam{

        private String orgBusinessUnitId;

        private Date todayDate;

        private String thisDay;

        private String thisMonth;

        private String thisYear;

        private String thisMonthTimestamp;

        private String monthBeginTime;

        private String todayEndTime;

        private String month;

        private Integer isMonthDataMark;

        private String enterpriseGroupOrgId;

        private List<String> commerceNykOrgIds;

        private Boolean isSyncInventory;

        private Boolean isFirstSyncReport;

    }

}
